#ifndef __Vector4f_H__
#define __Vector4f_H__

#include "Prerequisites.h"
#include "Math.h"
#include "Vec3f.h"
#include "Vec2f.h"


class Vector4f
{
    public:
        float x, y, z, w;

    public:
        inline Vector4f()
        {
        }

        inline Vector4f( const float fX, const float fY, const float fZ, const float fW )
            : x( fX ), y( fY ), z( fZ ), w( fW)
        {
        }

        inline explicit Vector4f( const float afCoordinate[4] )
            : x( afCoordinate[0] ),
            y( afCoordinate[1] ),
            z( afCoordinate[2] ),
            w( afCoordinate[3] )
    {
    }

        inline explicit Vector4f( const int afCoordinate[4] )
        {
            x = (float)afCoordinate[0];
            y = (float)afCoordinate[1];
            z = (float)afCoordinate[2];
            w = (float)afCoordinate[3];
        }

        inline explicit Vector4f( float* const r )
            : x( r[0] ), y( r[1] ), z( r[2] ), w( r[3] )
        {
        }

        inline explicit Vector4f( const float scaler )
            : x( scaler )
              , y( scaler )
              , z( scaler )
              , w( scaler )
    {
    }

        inline explicit Vector4f(const Vector3f& rhs)
            : x(rhs.x), y(rhs.y), z(rhs.z), w(1.0f)
        {
        }

        /** Exchange the contents of this vector with another. 
        */
        inline void swap(Vector4f& other)
        {
            std::swap(x, other.x);
            std::swap(y, other.y);
            std::swap(z, other.z);
            std::swap(w, other.w);
        }

        inline float operator [] ( const size_t i ) const
        {
            assert( i < 4 );

            return *(&x+i);
        }

        inline float& operator [] ( const size_t i )
        {
            assert( i < 4 );

            return *(&x+i);
        }

        /// Pointer accessor for direct copying
        inline float* ptr()
        {
            return &x;
        }
        /// Pointer accessor for direct copying
        inline const float* ptr() const
        {
            return &x;
        }

        /** Assigns the value of the other vector.
          @param
          rkVector The other vector
          */
        inline Vector4f& operator = ( const Vector4f& rkVector )
        {
            x = rkVector.x;
            y = rkVector.y;
            z = rkVector.z;
            w = rkVector.w;

            return *this;
        }

        inline Vector4f& operator = ( const float fScalar)
        {
            x = fScalar;
            y = fScalar;
            z = fScalar;
            w = fScalar;
            return *this;
        }

        inline bool operator == ( const Vector4f& rkVector ) const
        {
            return ( x == rkVector.x &&
                    y == rkVector.y &&
                    z == rkVector.z &&
                    w == rkVector.w );
        }

        inline bool operator != ( const Vector4f& rkVector ) const
        {
            return ( x != rkVector.x ||
                    y != rkVector.y ||
                    z != rkVector.z ||
                    w != rkVector.w );
        }

        inline Vector4f& operator = (const Vector3f& rhs)
        {
            x = rhs.x;
            y = rhs.y;
            z = rhs.z;
            w = 1.0f;
            return *this;
        }

        // arithmetic operations
        inline Vector4f operator + ( const Vector4f& rkVector ) const
        {
            return Vector4f(
                    x + rkVector.x,
                    y + rkVector.y,
                    z + rkVector.z,
                    w + rkVector.w);
        }

        inline Vector4f operator - ( const Vector4f& rkVector ) const
        {
            return Vector4f(
                    x - rkVector.x,
                    y - rkVector.y,
                    z - rkVector.z,
                    w - rkVector.w);
        }

        inline Vector4f operator * ( const float fScalar ) const
        {
            return Vector4f(
                    x * fScalar,
                    y * fScalar,
                    z * fScalar,
                    w * fScalar);
        }

        inline Vector4f operator * ( const Vector4f& rhs) const
        {
            return Vector4f(
                    rhs.x * x,
                    rhs.y * y,
                    rhs.z * z,
                    rhs.w * w);
        }

        inline Vector4f operator / ( const float fScalar ) const
        {
            assert( fScalar != 0.0 );

            float fInv = 1.0f / fScalar;

            return Vector4f(
                    x * fInv,
                    y * fInv,
                    z * fInv,
                    w * fInv);
        }

        inline Vector4f operator / ( const Vector4f& rhs) const
        {
            return Vector4f(
                    x / rhs.x,
                    y / rhs.y,
                    z / rhs.z,
                    w / rhs.w);
        }

        inline const Vector4f& operator + () const
        {
            return *this;
        }

        inline Vector4f operator - () const
        {
            return Vector4f(-x, -y, -z, -w);
        }

        inline friend Vector4f operator * ( const float fScalar, const Vector4f& rkVector )
        {
            return Vector4f(
                    fScalar * rkVector.x,
                    fScalar * rkVector.y,
                    fScalar * rkVector.z,
                    fScalar * rkVector.w);
        }

        inline friend Vector4f operator / ( const float fScalar, const Vector4f& rkVector )
        {
            return Vector4f(
                    fScalar / rkVector.x,
                    fScalar / rkVector.y,
                    fScalar / rkVector.z,
                    fScalar / rkVector.w);
        }

        inline friend Vector4f operator + (const Vector4f& lhs, const float rhs)
        {
            return Vector4f(
                    lhs.x + rhs,
                    lhs.y + rhs,
                    lhs.z + rhs,
                    lhs.w + rhs);
        }

        inline friend Vector4f operator + (const float lhs, const Vector4f& rhs)
        {
            return Vector4f(
                    lhs + rhs.x,
                    lhs + rhs.y,
                    lhs + rhs.z,
                    lhs + rhs.w);
        }

        inline friend Vector4f operator - (const Vector4f& lhs, float rhs)
        {
            return Vector4f(
                    lhs.x - rhs,
                    lhs.y - rhs,
                    lhs.z - rhs,
                    lhs.w - rhs);
        }

        inline friend Vector4f operator - (const float lhs, const Vector4f& rhs)
        {
            return Vector4f(
                    lhs - rhs.x,
                    lhs - rhs.y,
                    lhs - rhs.z,
                    lhs - rhs.w);
        }

        // arithmetic updates
        inline Vector4f& operator += ( const Vector4f& rkVector )
        {
            x += rkVector.x;
            y += rkVector.y;
            z += rkVector.z;
            w += rkVector.w;

            return *this;
        }

        inline Vector4f& operator -= ( const Vector4f& rkVector )
        {
            x -= rkVector.x;
            y -= rkVector.y;
            z -= rkVector.z;
            w -= rkVector.w;

            return *this;
        }

        inline Vector4f& operator *= ( const float fScalar )
        {
            x *= fScalar;
            y *= fScalar;
            z *= fScalar;
            w *= fScalar;
            return *this;
        }

        inline Vector4f& operator += ( const float fScalar )
        {
            x += fScalar;
            y += fScalar;
            z += fScalar;
            w += fScalar;
            return *this;
        }

        inline Vector4f& operator -= ( const float fScalar )
        {
            x -= fScalar;
            y -= fScalar;
            z -= fScalar;
            w -= fScalar;
            return *this;
        }

        inline Vector4f& operator *= ( const Vector4f& rkVector )
        {
            x *= rkVector.x;
            y *= rkVector.y;
            z *= rkVector.z;
            w *= rkVector.w;

            return *this;
        }

        inline Vector4f& operator /= ( const float fScalar )
        {
            assert( fScalar != 0.0 );

            float fInv = 1.0f / fScalar;

            x *= fInv;
            y *= fInv;
            z *= fInv;
            w *= fInv;

            return *this;
        }

        inline Vector4f& operator /= ( const Vector4f& rkVector )
        {
            x /= rkVector.x;
            y /= rkVector.y;
            z /= rkVector.z;
            w /= rkVector.w;

            return *this;
        }

        /** Calculates the dot (scalar) product of this vector with another.
          @param
          vec Vector with which to calculate the dot product (together
          with this one).
          @returns
          A float representing the dot product value.
          */
        inline float dotProduct(const Vector4f& vec) const
        {
            return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
        }
        /// Check whether this vector contains valid values
        inline bool isNaN() const
        {
            return Math::isNaN(x) || Math::isNaN(y) || Math::isNaN(z) || Math::isNaN(w);
        }
        /** Function for writing to a stream.
        */
        inline friend std::ostream& operator <<
            ( std::ostream& o, const Vector4f& v )
            {
                o << "Vector4f(" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ")";
                return o;
            }
        // special
        static const Vector4f ZERO;
};
#endif

